package com.example.project.projectspringcloud.controller.front.order;

import com.example.project.projectspringcloud.common.Constants;
import com.example.project.projectspringcloud.common.wechat.MessageUtil;
import com.example.project.projectspringcloud.common.wechat.PackageUtil;
import com.example.project.projectspringcloud.common.wechat.XMLParse;
import com.example.project.projectspringcloud.dao.sys.DictionaryMapper;
import com.example.project.projectspringcloud.entity.order.Order;
import com.example.project.projectspringcloud.entity.sys.Dictionary;
import com.example.project.projectspringcloud.entity.sys.SysConfig;
import com.example.project.projectspringcloud.service.order.AlipayService;
import com.example.project.projectspringcloud.service.order.OrderService;
import com.example.project.projectspringcloud.service.order.WeChatPayService;
import com.example.project.projectspringcloud.service.sys.SysConfigService;
import com.example.project.projectspringcloud.utils.DateUtil;
import com.example.project.projectspringcloud.utils.HttpUtil;
import com.example.project.projectspringcloud.utils.ObjectUtil;
import com.example.project.projectspringcloud.utils.WebUtil;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * 支付方法不通过spring-security 验证
 */
@Controller
@RequestMapping(value = "/pay")
public class PayController {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private OrderService orderService;

    @Autowired
    private AlipayService alipayService;

    @Autowired
    private SysConfigService sysConfigService;

    @Autowired
    private DictionaryMapper dictionaryMapper;

    @Autowired
    private WeChatPayService weChatPayService;

    @ApiOperation(value = "支付完成回调方法", notes = "支付宝、微信支付完成回调方法")
    @RequestMapping(value = "/callback/{businessType}/{method}")
    public String callback(@PathVariable("businessType") Integer businessType,
                           @PathVariable("method") String method,
                           HttpServletRequest request, HttpServletResponse response,
                           RedirectAttributes redirectAttributes) throws Exception {
        switch (businessType) {
            // 支付宝
            case 1:
                if (alipayService.getAlipayCheckInfo(request)) {
                    // 本系统中订单流水号
                    String outTradeNo = request.getParameter("out_trade_no");
                    logger.info("Alipay callback outTradeNo: {}", outTradeNo);
                    // 支付宝交易号
                    String tradeNo = request.getParameter("trade_no");
                    logger.info("Alipay callback tradeNo is: {}", tradeNo);

                    // 总价
                    String totalFee = request.getParameter("total_fee");
                    // 订单说明
                    String body = request.getParameter("body");
                    logger.info("Alipay callback body: {}", body);
                    body = new String(request.getParameter("body").getBytes("ISO-8859-1"), "UTF-8");
                    // 扩展信息
                    String extraCommonParam = request.getParameter("extra_common_param");
                    // 返回给支付宝成功信息
                    if (request.getParameter("trade_status").equals("WAIT_BUYER_PAY")) {
                        // 等待买家付款
                        response.setCharacterEncoding("UTF-8");
                        response.getWriter().write("fail");
                        logger.info("Alipay callback, WAIT_BUYER_PAY");
                    } else if (request.getParameter("trade_status").equals("TRADE_FINISHED")
                            || request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
                        // 校验好状态, 在这里可以写入订单的数据处理,
                        Map<String, String> aliRequestParams = new HashMap<String, String>();
                        aliRequestParams.put("total_fee", totalFee);
                        aliRequestParams.put("out_trade_no", outTradeNo);
                        aliRequestParams.put("userId", extraCommonParam.split(",")[0]);
                        aliRequestParams.put("requestId", extraCommonParam.split(",")[1]);
                        aliRequestParams.put("payType", Constants.PayMethod.ALIPAY.toString());
                        String[] orderArr = extraCommonParam.split(",")[1].split("#");

                        // 查询本地订单记录
                        Order order = new Order();
                        order.setNo(orderArr[0]);
                        order = orderService.get(order);

                        // 如果已完成支付
                        if (Constants.OrderStatus.PAID == order.getStatus()) {
                            // 如果是支付宝提醒通知请求
                            if ("notify".equals(method)) {
                                return null;
                            } else {
                                return String.format("redirect:/order/get/%s", order.getId());
                            }
                        }
                        // 校验金额, 完成支付
                        else {
                            logger.info("Alipay callback - businessType: {} - method: {} - res: {}", businessType, method, aliRequestParams);
                            try {
                                if (order.getTotalPrice().equals(new BigDecimal(totalFee))) {
                                    String payTime = request.getParameter("notify_time");
                                    order.setPayTime(DateUtil.parseToDate(payTime, "yyyy-MM-dd HH:mm:ss"));
                                    order.setStatus(Constants.OrderStatus.PAID);
                                    order.setPayType(Constants.PayMethod.ALIPAY.ordinal());
                                    orderService.save(order);

                                    // response.setCharacterEncoding("UTF-8");
                                    // response.getWriter().write("success");
                                    // 订单号
                                    // redirectAttributes.addAttribute("orderNo", order.getNo());
                                    return "redirect:/order/list/1";
                                } else {
                                    logger.error(String.format("Alipay callback error! - businessType: %s - method: %s - aliRequest: %s", businessType, method, aliRequestParams));
                                    // response.setCharacterEncoding("UTF-8");
                                    // response.getWriter().write("fail");
                                    return "/500";
                                }
                            } catch (Exception e) {
                                logger.error(String.format("Alipay callback error! - businessType: %s - method: %s - aliRequest: %s", businessType, method, aliRequestParams), e);
                                // response.setCharacterEncoding("UTF-8");
                                // response.getWriter().write("fail");
                                return "/500";
                            }

                        }

                    }
                }
                break;

            // 微信
            case 2:
                // 解析微信支付返回内容
                Map<String, String> requestMap = MessageUtil.parseXml(request);
                // 获取本地微信支付配置
                Map<String, String> weChatConfigs = sysConfigService.configs(new SysConfig(Constants.SysConfigType.SYS_CONFIG_TYPE_PAY_WECHAT));

                /**
                 * 支付第一阶段, 验证商品
                 */
                String productId = requestMap.get("product_id");
                if (StringUtils.isNotBlank(productId)) {
                    // payKey
                    String payKey = weChatConfigs.get("wxPayKey");
                    // 验证微信回调的签名
                    if (checkSignature(requestMap, payKey)) {
                        String requestId = productId.split("#")[0];
                        Order entity = new Order();
                        entity.setNo(requestId);
                        Order order = orderService.get(entity);
                        // 订单描述
                        String description = String.format("%s: %s - %s", Constants.Order.COMPANY_NAME,
                                dictionaryMapper.selectOne(new Dictionary("order-type", order.getType())).getName(),
                                dictionaryMapper.selectOne(new Dictionary("video-type", order.getOrderDetail().getVideoType())).getName());
                        // 返回商品信息
                        SortedMap<String, String> packageParams = weChatPayService.createPackage(description, productId, productId, order.getTotalPrice(),
                                order.getUserId(), WebUtil.getIpAddr(request), "/pay/callback/2/payment",
                                weChatConfigs, "", "NATIVE", request);

                        // sign签名
                        String sign = PackageUtil.createSign(packageParams, payKey);
                        // 统一支付请求参数xml
                        String xmlPackage = XMLParse.generateXmlPackage(packageParams, sign);
                        // 预支付订单创建结果
                        String resultXmlStr = HttpUtil.doPostXml("https://api.mch.weixin.qq.com/pay/unifiedorder", xmlPackage);
                        Map<String, String> resultMap = MessageUtil.parseStringXml(resultXmlStr);
                        // 通信标识返回状态码
                        String returnCode = resultMap.get("return_code");
                        // 返回状态消息
                        String returnMsg = resultMap.get("return_msg");
                        if (returnCode.equalsIgnoreCase("FAIL")) {
                            logger.info("return_code-----fail:" + returnMsg);
                        } else {
                            // 业务结果
                            String result_code = resultMap.get("result_code");
                            if (result_code.equalsIgnoreCase("FAIL")) {
                                logger.info("result_code-----fail:err_code" + resultMap.get("err_code") + "/n" + "err_code_des" + resultMap.get("result_code"));
                                logger.info("err_code:" + resultMap.get("err_code"));
                                logger.info("err_code_des:" + resultMap.get("err_code_des"));
                            } else {
                                String prepay_id = resultMap.get("prepay_id");
                                // 返回微信消息
                                // 请求参数包
                                SortedMap<String, String> returnPackageParams = returnPackage(requestId, prepay_id, order.getStatus(), weChatConfigs);
                                // sign签名
                                String returnSign = PackageUtil.createSign(returnPackageParams, payKey);
                                // 统一支付请求参数xml
                                String xmlReturnPackage = XMLParse.returnXmlPackage(returnPackageParams, returnSign);
                                OutputStream os = response.getOutputStream();
                                BufferedWriter resBr = new BufferedWriter(new OutputStreamWriter(os));
                                resBr.write(xmlReturnPackage);
                                resBr.flush();
                                resBr.close();
                            }
                        }
                    }
                }

                // 支付完成
                if (method.equals("payment")) {
                    // 验证签名，参数签名及xml签名、通信成功
                    if (checkSignature(requestMap, (String) weChatConfigs.get("wxPayKey"))
                            && "SUCCESS".equals(requestMap.get("return_code"))) {
                        if (requestMap.get("result_code").equals("SUCCESS")) {
                            // 可以在不同状态下获取订单信息，操作商户数据库使数据同步
                            // 订单编号,非requestId，给微信的payId
                            String outTradeNo = requestMap.get("out_trade_no");
                            logger.info("WeChat pay callback outTradeNo: {}", outTradeNo);
                            // 微信交易号
                            String transaction_id = requestMap.get("transaction_id");
                            logger.info("WeChat pay callback transaction_id: {}", transaction_id);
                            // 总价 微信是分为单位，需要转化为元
                            String totalFee = requestMap.get("total_fee");
                            totalFee = new BigDecimal(totalFee).divide(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP).toString();
                            // 附加数据 用户id，订单requestId,订单类型
                            String attach = requestMap.get("attach");
                            // 校验好状态,在这里可以写入订单的数据处理,
                            Map<String, String> sourceMap = new HashMap<String, String>();
                            sourceMap.put("total_fee", totalFee);
                            sourceMap.put("out_trade_no", outTradeNo);
                            sourceMap.put("requestId", attach.split(",")[0].split("#")[0]);
                            sourceMap.put("userId", attach.split(",")[1]);
                            sourceMap.put("payType", Constants.PayMethod.WECHAT.toString());

                            // 查询本地订单记录
                            Order order = new Order();
                            order.setNo(attach.split(",")[0].split("#")[0]);
                            order = orderService.get(order);

                            if (order.getStatus() == Constants.OrderStatus.PAID) {
                                // 注意一定要返回给微信一个成功的信息(不包含HTML脚本语言)
                                sendXmlMessage(request, response, "SUCCESS");
                            } else {
                                // 必须校验支付的金额
                                if (order.getTotalPrice().equals(new BigDecimal(totalFee))) {

                                }
                                Map<String, String> res = orderService.validate(sourceMap);
                                if (ObjectUtil.isNotNull(res)) {
                                    // 根据返回的结果，订单支付成功或者充值成功时，代表成功
                                    if (res.get("status").equalsIgnoreCase("success")) {
                                        // 更新订单状态
                                        order.setPayTime(new Date());
                                        order.setStatus(Constants.OrderStatus.PAID);
                                        order.setPayType(Constants.PayMethod.WECHAT.ordinal());
                                        if (orderService.save(order)) {
                                            // 注意一定要返回给微信一个成功的信息(不包含HTML脚本语言)
                                            sendXmlMessage(request, response, "SUCCESS");
                                        } else {
                                            logger.info("WeChat Pay Fail!");
                                            // 失败
                                            sendXmlMessage(request, response, "FAIL");
                                        }

                                    } else {
                                        logger.info("WeChat Pay Fail!");
                                        // 失败
                                        sendXmlMessage(request, response, "FAIL");
                                    }
                                } else {
                                    logger.info("WeChat Pay Fail!");
                                    // 失败
                                    sendXmlMessage(request, response, "FAIL");
                                }
                            }

                        } else {
                            logger.error("WeChat Pay fail result_code error");
                            logger.info("err_code: {}", requestMap.get("return_code"));
                            logger.info("err_code_des: {}", requestMap.get("err_code_des"));
                            sendXmlMessage(request, response, "FAIL");
                        }
                    } else {
                        logger.error("WeChat Pay AppSignature or return_code error");
                        redirectAttributes.addAttribute("msg", "请求异常");
                        sendXmlMessage(request, response, "FAIL");
                    }
                }

                break;
            default:
                break;
        }

        return null;
    }

    /**
     * 微信预支付订单返回参数
     */
    public SortedMap<String, String> returnPackage(String requestId, String prepay_id, int trxStatus, Map<String, String> websitemap) {
        // 设置package参数
        SortedMap<String, String> packageParams = new TreeMap<String, String>();
        packageParams.put("return_code", "SUCCESS");// 通信状态
        String noncestr = PackageUtil.getNonceStr();// 随机字符串
        packageParams.put("appid", websitemap.get("wxAppID")); // appid
        packageParams.put("mch_id", websitemap.get("wxMchId")); // 商户号id
        packageParams.put("nonce_str", noncestr);
        packageParams.put("prepay_id", prepay_id);
        String retErrMsg = "";

        // 支付成功
        if (trxStatus == Constants.OrderStatus.PAID) {
            retErrMsg = "订单已支付完成";
        } else if (trxStatus == Constants.OrderStatus.CANCEL) {
            retErrMsg = "订单已取消";
        }
        // 错误信息
        packageParams.put("err_code_des", retErrMsg);
        String resultCode = "SUCCESS";
        // 错误消息不为空，返回失败状态，直接显示错误消息
        if (!retErrMsg.equals("")) {
            resultCode = "FAIL";
        }
        packageParams.put("result_code", resultCode);
        return packageParams;
    }

    /**
     * 验证回调签名
     */
    public boolean checkSignature(Map<String, String> requestMap, String payKey) {
        SortedMap<String, String> signParams = new TreeMap<String, String>();
        String signature = requestMap.get("sign");
        if (StringUtils.isBlank(signature)) {
            return false;
        }
        requestMap.remove("sign");
        for (Map.Entry<String, String> entry : requestMap.entrySet()) {// 循环放入树Map
            signParams.put(entry.getKey(), entry.getValue());
        }
        try {
            String sign = PackageUtil.createSign(signParams, payKey);
            if (signature.equalsIgnoreCase(sign)) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return false;
    }

    private void sendXmlMessage(HttpServletRequest request, HttpServletResponse response, String content) throws IOException {
        try {
            String contentXml = "<xml><return_code><![CDATA[" + content + "]]></return_code></xml>";
            OutputStream os = response.getOutputStream();
            BufferedWriter resBr = new BufferedWriter(new OutputStreamWriter(os));
            resBr.write(contentXml);
            resBr.flush();
            resBr.close();
        } catch (Exception e) {
            logger.error("Send Message To WeChat", e);
        }
    }
}
